package info.wangyuan.agent.utils;

import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 跨平台文件夹元数据管理工具（Windows/Linux/macOS）
 * 元数据存储在文件夹下隐藏文件 .folder_meta.json
 *
 * @author Albert
 * @since 2025-08-16
 */
@Slf4j
public class FolderMetaUtils {

    private static final String META_FILENAME = ".folder_meta.json";

    /**
     * 元数据键常量
     */
    private static final String META_IS_USED = "isUsed";
    private static final String META_RELATIVE_AGENT = "relativeAgent";

    /**
     * 私有构造函数，防止实例化
     */
    private FolderMetaUtils() {
    }

    /**
     * 读取文件夹元数据，如果不存在则创建默认元数据
     */
    public static Map<String, Object> loadOrCreateMetadata(Path folder) throws IOException {
        if (hasMetadata(folder)) {
            log.debug("加载文件夹 [{}] 的元数据", folder);
            return readMetadata(folder);
        } else {
            log.info("文件夹 [{}] 未发现元数据，创建默认元数据", folder);
            Map<String, Object> defaultMeta = Map.of(
                    META_IS_USED, false,
                    META_RELATIVE_AGENT, new HashSet<Integer>()
            );
            saveMetadata(folder, defaultMeta);
            return defaultMeta;
        }
    }

    /**
     * 向文件夹元数据添加单个 agentId
     */
    public static boolean addAgentIdToFolder(Path folder, Integer agentId) throws IOException {
        if (agentId == null) {
            log.warn("尝试向文件夹 [{}] 添加 null agentId，已忽略", folder);
            return false;
        }
        return addAgentIdsToFolder(folder, Set.of(agentId));
    }

    /**
     * 批量向文件夹元数据添加 agentId
     */
    public static boolean addAgentIdsToFolder(Path folder, Set<Integer> agentIds) throws IOException {
        if (agentIds == null || agentIds.isEmpty()) {
            log.warn("尝试向文件夹 [{}] 添加空 agentIds，已忽略", folder);
            return false;
        }

        Map<String, Object> meta = loadOrCreateMetadata(folder);
        Set<Integer> agents = JsonUtils.getIntegerSet(meta, META_RELATIVE_AGENT);

        boolean changed = agents.addAll(agentIds);

        // 更新 isUsed
        boolean isUsed = JsonUtils.getBoolean(meta, META_IS_USED, false);
        if (!isUsed) {
            log.info("文件夹 [{}] 首次被标记为 isUsed=true", folder);
            meta.put(META_IS_USED, true);
            changed = true;
        }

        // 如果有任何变更，更新 Map 并保存
        if (changed) {
            meta.put(META_RELATIVE_AGENT, agents);
            saveMetadata(folder, meta);
            log.info("文件夹 [{}] 的元数据已更新，添加 agentIds: {}", folder, agentIds);
        } else {
            log.debug("文件夹 [{}] 的元数据未发生变化", folder);
        }

        return changed;
    }

    /**
     * 从文件夹元数据中删除单个 agentId
     */
    public static boolean removeAgentIdFromFolder(Path folder, Integer agentId) throws IOException {
        if (agentId == null) {
            log.warn("尝试从文件夹 [{}] 删除 null agentId，已忽略", folder);
            return false;
        }
        return removeAgentIdsFromFolder(folder, Set.of(agentId));
    }

    /**
     * 批量从文件夹元数据中删除 agentId
     */
    public static boolean removeAgentIdsFromFolder(Path folder, Set<Integer> agentIds) throws IOException {
        if (agentIds == null || agentIds.isEmpty()) {
            log.warn("尝试从文件夹 [{}] 删除空 agentIds，已忽略", folder);
            return false;
        }

        Map<String, Object> meta = loadOrCreateMetadata(folder);
        Set<Integer> agents = JsonUtils.getIntegerSet(meta, META_RELATIVE_AGENT);

        boolean changed = agents.removeAll(agentIds);

        // 如果有变化则保存
        if (changed) {
            meta.put(META_RELATIVE_AGENT, agents);
            // 如果删除后集合为空，可以考虑把 isUsed 改为 false
            if (agents.isEmpty()) {
                log.info("文件夹 [{}] 的 agentIds 清空，设置 isUsed=false", folder);
                meta.put(META_IS_USED, false);
            }
            saveMetadata(folder, meta);
            log.info("文件夹 [{}] 的元数据已更新，删除 agentIds: {}", folder, agentIds);
        } else {
            log.debug("文件夹 [{}] 的元数据未发生变化（要删除的 agentIds 不存在）", folder);
        }

        return changed;
    }


    /**
     * 保存或更新文件夹元数据，并设置跨平台隐藏和最大权限
     */
    public static void saveMetadata(Path folder, Map<String, Object> metadata) throws IOException {
        checkFolder(folder);
        Path metaFile = folder.resolve(META_FILENAME);

        // 写 JSON
        JsonUtils.writeJsonToFile(metaFile, metadata);

        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("win")) {
            File file = metaFile.toFile();
            file.setReadable(true, false);
            file.setWritable(true, false);
        } else {
            // Linux/macOS 下设置 777 权限
            Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxrwxrwx");
            Files.setPosixFilePermissions(metaFile, perms);
            log.debug("在 Linux/macOS 上为元数据文件 [{}] 设置 777 权限", metaFile);
        }

        log.info("文件夹 [{}] 的元数据保存成功: {}", folder, metaFile);
    }

    /**
     * 读取文件夹元数据
     */
    public static Map<String, Object> readMetadata(Path folder) {
        checkFolder(folder);
        Path metaFile = folder.resolve(META_FILENAME);

        if (!Files.exists(metaFile)) {
            log.warn("读取元数据失败，文件不存在: {}", metaFile);
            return Collections.emptyMap();
        }

        Map<String, Object> meta = JsonUtils.parseJsonToMap(metaFile);
        if (CollUtil.isEmpty(meta)) return Collections.emptyMap();

        log.debug("成功读取文件夹 [{}] 的元数据: {}", folder, meta);
        return meta;
    }

    /**
     * 删除文件夹元数据
     */
    public static void deleteMetadata(Path folder) throws IOException {
        checkFolder(folder);
        Path metaFile = folder.resolve(META_FILENAME);
        if (Files.exists(metaFile)) {
            Files.delete(metaFile);
            log.info("已删除文件夹 [{}] 的元数据文件: {}", folder, metaFile);
        } else {
            log.debug("文件夹 [{}] 不存在元数据文件，无需删除", folder);
        }
    }

    /**
     * 判断文件夹是否有元数据
     */
    public static boolean hasMetadata(Path folder) {
        checkFolder(folder);
        boolean exists = Files.exists(folder.resolve(META_FILENAME));
        log.debug("检查文件夹 [{}] 是否有元数据: {}", folder, exists);
        return exists;
    }

    public static Set<Integer> getAgentIdsSet(Path folder) throws IOException {
        Map<String, Object> meta = loadOrCreateMetadata(folder);
        return JsonUtils.getIntegerSet(meta, META_RELATIVE_AGENT);
    }

    /**
     * 检查路径是否为有效文件夹
     */
    private static void checkFolder(Path folder) {
        if (folder == null || !Files.isDirectory(folder)) {
            log.error("路径不是有效的文件夹: {}", folder);
            throw new IllegalArgumentException("不是有效的文件夹: " + folder);
        }
    }
}
